昨天將產出的驗證碼寫進了 Google Sheet,但我們還需要另一個功能:輸入驗證碼找出所在Row,並且在同一列的其他 column 寫入綁定的 userId 和 綁定時間。這樣使用者在 Line Bot 輸入驗證碼後,我們才能知道綁定這個驗證碼的使用者是誰。
因為產出的驗證碼放在 verification_code
的第一行,所以我們可以取出這一行每個儲存格的值,再查找目標驗證碼的index,從而可以得知該驗證碼所在列號。
新增一個 tagVerificationCode.gs 內容如下:
function tagVerificationCode() {
var target = 'a55969eb';
// 連接到 verification_code sheet
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
// 取得從 A1 開始到最後一列有資料的 AX 的所有值,X = getLastRow()
var columnValues = sheet.getRange(1, 1, sheet.getLastRow(), 1).getValues();
// 利用 findIndex 遍歷 columnValues 查找 target 驗證碼,找到的話回傳 index,否則回傳 -1
var searchResult = columnValues.findIndex((element)=>element[0]==target);
Logger.log(searchResult);
}
將 target 的值改為想查找的驗證碼,然後執行看看結果:
然後進一步將 tagVerificationCode.gs 修改如下:
function tagVerificationCode(target, userId) {
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
var columnValues = sheet.getRange(1, 1, sheet.getLastRow(), 1).getValues();
var searchResult = columnValues.findIndex((element)=>element[0]==target);
if (searchResult !== -1) {
searchResult++;
var targetRange = sheet.getRange(searchResult, 2, 1, 2);
targetRange.setValues([[userId, new Date()]]);
}
}
修改後記得儲存,然後選取要執行的函式為 testTagVerificationCode
按下執行後查看結果:
試算表結果:
先前有提過,Google Sheet 雖然可以當成簡易資料庫使用,但其實是沒有 Query & Insert 的概念的。而我們剛剛完成的 tagVerificationCode.gs 裡用來查找某行資料有沒有這個值,其實就有點類似 Query 的概念,所以很適合抽成共用 function 方便之後重複使用。
在 Read Mail
專案內新增 searchColumnValue.gs
function searchColumnValue(sheet, columnName, target) {
Logger.log('start to searchColumnValue');
// 先取得標題列的值
var headerRowValue = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
// 查看 columnName 在標題列的 index
var columnIndex = headerRowValue[0].indexOf(columnName);
if (columnIndex !== -1) {
columnIndex++;
// 抓出 columnName 那行所有的值
var columnValues = sheet.getRange(2, columnIndex, sheet.getLastRow(), 1).getValues();
// 回傳查找結果
var searchResult = columnValues.findIndex((element)=>element[0]==target);
return searchResult;
}
}
因為已經部署過 Read Mail
了,有修改的話要管理部署 > 編輯 > 增加新版本
在 Reply Message
的資料庫也要跟著切換版本,看看是否有正確讀到最新版。有時候如果沒有自動同步到最新開發人員版本,可以先切成別的版本再切回來就會正常。
加上標題列如下:
因為增加了標題列,所以 Reply Message
的 generateVerificationCode 跟 tagVerificationCode 也需要稍作修改,避免寫入的範圍有誤差
修改 generateVerificationCode.gs 的 insertVerificationCode 如下:
function insertVerificationCode(uniqueVerificationCode) {
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
var range = sheet.getRange(2, 1, uniqueVerificationCode.length, 1);
range.setValues(uniqueVerificationCode);
}
修改 tagVerificationCode.gs 如下:
function tagVerificationCode(target, userId) {
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
var searchResult = ReadMailAndInsertToGoogleSheet.searchColumnValue(sheet, 'code', target);
if (searchResult !== -1) {
var targetRange = sheet.getRange((searchResult+2), 2, 1, 2);
targetRange.setValues([[userId, new Date()]]);
}
}
如此一來即可重複引用這個寫好的查找功能~
Reply Message
加上身份驗證接著讓我們對 Reply Message
的流程稍作修改,加上簡單的身份驗證流程吧
Reply Message
收到 Message Event獲取驗證碼
用來判斷這個 userId 是否已經存在我們的綁定紀錄裡
function isUserIdVerified(userId) {
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
var searchResult = ReadMailAndInsertToGoogleSheet.searchColumnValue(sheet, 'user_id', userId);
return (searchResult !== -1);
}
將 replyMessage.gs 修改以符合我們的新流程:
const CHANNEL_ACCESS_TOKEN = 'YOUR_CHANNEL_ACCESS_TOKEN';
function doPost(e) {
var requestContent = JSON.parse(e.postData.contents);
var event = requestContent.events[0];
// 必須是 Message Event
if (event && (event.type === 'message')) {
var replyToken = event.replyToken;
var userId = event.source && event.source.userId;
var userMessage = event.message.text;
var replyMessage = [];
if(isUserIdVerified(userId)) {
replyMessage = userIsVerifiedFlow(userMessage, userId);
} else {
replyMessage = userIsNotVerifiedFlow(userMessage, userId);
}
doReplyMessage(replyMessage, replyToken);
}
return ContentService.createTextOutput('success');
}
function userIsVerifiedFlow(userMessage, userId){
return (userMessage === '獲取驗證碼') ? getValidationCodeMessage(userId) : getReplyMessage('無效的輸入');
}
function userIsNotVerifiedFlow(userMessage, userId){
var bindResult = tagVerificationCode(userMessage, userId);
var replyMessage = bindResult ? '綁定成功!請點擊選單或輸入獲取驗證碼。' : '請先進行身分認證綁定。'
return getReplyMessage(replyMessage);
}
function getValidationCodeMessage(userId) {
var validationCode = ReadMailAndInsertToGoogleSheet.app(userId);
return [{
'type': 'text',
'text': validationCode
}];
}
function getReplyMessage(message) {
return [{
'type': 'text',
'text': message
}];
}
function doReplyMessage(replyMessage, replyToken) {
var payload = {
replyToken: replyToken,
messages: replyMessage
};
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN
},
'method': 'post',
'payload': JSON.stringify(payload)
});
}
tagVerificationCode 也要稍作修改,增加檢查驗證碼是否已被綁定過(user_id 不為空),以符合我們的新流程:
function tagVerificationCode(target, userId) {
var sheet = ReadMailAndInsertToGoogleSheet.connectToSheet('verification_code');
var searchResult = ReadMailAndInsertToGoogleSheet.searchColumnValue(sheet, 'code', target);
if (searchResult !== -1) {
var targetRowIndex = searchResult+2;
var userIdValue = sheet.getRange(targetRowIndex, 2, 1, 1).getValue();
if (userIdValue.length === 0) {
var targetRange = sheet.getRange((searchResult+2), 2, 1, 2);
targetRange.setValues([[userId, new Date()]]);
return true;
}
}
return false;
}
修改完後一樣要管理部署 > 編輯 > 建立新版本 如下:
接著把網頁應用程式的網址複製,更新到驗證碼小幫手的 Messaging Api Webhook 網址
忘記怎麼設置的話請看 部署 Google App Script 專案(2) & Line Bot 簡單回應訊息
設定好後就可以用驗證碼小幫手測試看看結果囉~
結果如下圖:
Google Sheet 的結果如下:
以上~這樣就完成了簡單的身份驗證流程。
當然還有很多可以完善的地方,例如雖然可以交由程式自動產生驗證碼跟綁定,但卻要我們手動發送驗證碼給使用者,使用者也只能手動輸入,實在是太不貼心~但這就是我們可以學習進步的地方!明天要做什麼還尚待決定~差不多也該進入 Liff 應用的篇幅了,那麼就待明天再揭曉主題吧~